package cn.bonoon.kernel.bytecode.asm;

import java.lang.reflect.Method;

import javax.persistence.EntityManager;

import org.springframework.asm.MethodVisitor;
import org.springframework.asm.Opcodes;
import org.springframework.asm.Type;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.util.ClassUtils;

import cn.bonoon.kernel.IdGenerator;
import cn.bonoon.kernel.descriptors.ClassDescriptor;
import cn.bonoon.kernel.descriptors.EntityDescriptor;
import cn.bonoon.kernel.descriptors.ObjectDescriptor;
import cn.bonoon.kernel.descriptors.ObjectPropertyAccessor;
import cn.bonoon.kernel.support.entities.Persistable;

interface ASMBuilder extends Opcodes{
	
	/**
	 * <pre>
	 * 用来取得spring配置文件里引用的配置文件里定义的配置参数
	 * 如：global.properties文件里定义的参数等
	 * </pre>
	 * @param key
	 * @return
	 */
	String resolveEmbeddedValue(String key);
	String assignField(String fieldDes);
	MethodVisitor getConstructor();
	String getClassPath();
}

abstract class ASMAbstractBuilder<R, D extends ClassDescriptor> extends AbstractClassWriter implements ASMBuilder{
	protected final String managerPath;
	protected final String entityPath;
	protected final String objectPath;
	
//	protected final int thisPoint = 0;
	
	protected final D objectDescriptor;
	protected final Class<R> parentClass;
	private final ApplicationContext applicationContext;
	
	@Override
	public String getClassPath() {
		return classPath;
	}
	
	@Override
	public MethodVisitor getConstructor() {
		return constructor;
	}
	
	@Override
	public String resolveEmbeddedValue(String key){
		return ((ConfigurableApplicationContext)applicationContext).getBeanFactory().resolveEmbeddedValue("${" + key + "}");
	}
	
//	private final static String DEFAULT_CLASS_PACKAGE = "cn.bonoon.asm.auto.";
	
	protected ASMAbstractBuilder(ApplicationContext applicationContext, Class<R> parentClass, Class<?> entityClass, D objectDescriptor){
		this.applicationContext = applicationContext;
		this.objectDescriptor 	= objectDescriptor;
		this.parentClass 		= parentClass;
		
		this.objectPath 		= Type.getInternalName(objectDescriptor.toClass());
		this.entityPath 		= Type.getInternalName(entityClass);
		this.managerPath 		= Type.getInternalName(EntityManager.class);
		this.parentPath 		= Type.getInternalName(parentClass);
		
		this.className 			= parentClass.getName() + IdGenerator.build();
		this.classPath 			= ClassUtils.convertClassNameToResourcePath(this.className);
	}
	
	@SuppressWarnings("unchecked")
	public final R build() throws Exception{
        return (R)buildClass().getDeclaredConstructor().newInstance();
	}
	
	protected MethodVisitor _visitMethod(String name, Class<?>... parameterTypes)throws Exception{
		Method method = parentClass.getDeclaredMethod(name, parameterTypes);
		MethodVisitor visitor = visitMethod(ACC_PUBLIC, method.getName(), Type.getMethodDescriptor(method) , null, new String[]{"java/lang/Throwable"});
		visitor.visitCode();
		return visitor;
	}
	
	protected void _return(MethodVisitor visitor, int ret){
		visitor.visitInsn(ret);
		visitor.visitMaxs(0, 0);
		visitor.visitEnd();
	}
	/**
	 * 分配一个变量
	 * @param desc
	 * @return 自动分配的变量名
	 */
	@Override
	public String assignField(String desc){
		return assignField(ACC_PROTECTED, desc, null, null);
	}
}

abstract class ASMAbstractConvertBuilder<R> extends ASMAbstractBuilder<R, ObjectDescriptor>{
	
	protected final String persistePath;
	protected final String eventPath;
	protected final Class<?> eventClass;
//	protected int eventPoint;
	protected final EntityDescriptor entityDescriptor;
	
	protected ASMAbstractConvertBuilder(ApplicationContext applicationContext, Class<R> parentClass, ObjectDescriptor objectDescriptor, EntityDescriptor entityDescriptor, Class<?> eventClass){
		super(applicationContext, parentClass, entityDescriptor.toClass(), objectDescriptor);
		this.eventClass = eventClass;
		this.eventPath = Type.getInternalName(eventClass);
		this.persistePath = Type.getInternalName(Persistable.class);
		this.entityDescriptor = entityDescriptor;
	}
	
	public String entityPath() {
		return entityPath;
	}
	
	public String objectPath() {
		return objectPath;
	}
	
	public String entityName() {
		return entityDescriptor.getName();
	}
	
	public boolean isBase() {
		return entityDescriptor.is(EntityDescriptor.BASE);
	}
	
	public void visitMethodFind(MethodVisitor visitor) {
		visitor.visitMethodInsn(INVOKEINTERFACE, managerPath, "find", "(Ljava/lang/Class;Ljava/lang/Object;)Ljava/lang/Object;", true);
	}
	
	public void visitMethodPersist(MethodVisitor visitor) {
		visitor.visitMethodInsn(INVOKEINTERFACE, managerPath, "persist", "(Ljava/lang/Object;)V", true);
	}
}

/**
 * 当前正在处理的属性的处理事件
 * @author jackson
 *
 */
class ASMEvent{
	
	/**
	 * <pre>
	 * 写为实体(普通实体或数据库实体)的方法
	 * 
	 * read  : writer -> object setter method
	 * write : writer -> entity setter method
	 * </pre>
	 */
	protected final MethodVisitor writer;
	
	/**
	 * <pre>
	 * 读取字段的getter方法的指针：
	 * read  : getterPoint -> entity getter method point
	 * write : getterPoint -> object getter method point
	 * </pre>
	 */
	protected final int getterPoint;
	
	/**
	 * <pre>
	 * 写字段的setter方法的指针：
	 * read  : setterPoint -> object setter method point
	 * write : setterPoint -> entity setter method point
	 * </pre>
	 */
	protected final int setterPoint;
	
	/**
	 * 当前变量的指针
	 */
	private int point;
	
//	protected final String builderClassPath;
	
	protected ASMEvent( MethodVisitor writer, int setterPoint, int getterPoint, int assignPoint){
//		this.builderClassPath = builderClassPath;
		this.writer = writer;
		this.getterPoint = getterPoint;
		this.setterPoint = setterPoint;
		this.point = assignPoint;
	}
	
	/**
	 * 分配变量指针，只有当需要在方法体里创建变量的时候才调用，不应该在循环内分配变量指针
	 * @return
	 */
	protected int assignPoint(){
		return point++;
	}
	
	public void visitInsn(int arg0) {
		writer.visitInsn(arg0);
	}
}

interface ASMProperty extends Opcodes{
	void parseProperty(ASMEvent event) throws Exception;
}

abstract class ASMAbstractProperty implements ASMProperty{
	
	protected final String name;
	
	/**
	 * <pre>
	 * read  : object -> setter 向object实体写数据
	 * write : object -> getter 从object实体读数据写入entity实体
	 * </pre>
	 */
	protected final ObjectPropertyAccessor objectProperty;
	
	/**
	 * 应该支持懒加载；从数据库实体读值的时候，该字段无效
	 */
	protected final boolean ignoreNull;
	
	/**
	 * <pre>
	 * read  : getterType -> entity getter
	 * write : getterType -> object getter
	 * </pre>
	 */
	protected Class<?> getterType;
	
	/**
	 * <pre>
	 * read  : setterType -> object setter
	 * write : setterType -> entity setter
	 * </pre>
	 */
	protected Class<?> setterType;
	
	protected ASMAbstractProperty(String name, ObjectPropertyAccessor objectProperty, boolean ignoreNull){
		this.name = name;
		this.ignoreNull = ignoreNull;
		this.objectProperty = objectProperty;
	}
	
	protected abstract void loadSetter(ASMEvent event);
	protected abstract void visitSetter(MethodVisitor visitor);
	protected abstract void loadGetter(ASMEvent event);
	protected abstract void visitGetter(MethodVisitor visitor);
}

class ASMMethod{
	
	protected final String path;
	protected final String name;
	protected String des;
	
	protected ASMMethod(Class<?> cls, Method method){
		this(Type.getInternalName(cls), method.getName());
		this.des = Type.getMethodDescriptor(method);
	}
	
	protected ASMMethod(String path, Method method){
		this(path, method.getName());
		this.des = Type.getMethodDescriptor(method);
	}
	
	protected ASMMethod(String path, String name, String des){
		this(path, name);
		this.des = des;
	}
	
	protected ASMMethod(String path, String name){
		this.path = path;
		this.name = name;
	}
	
	protected ASMMethod(Class<?> cls, String name, Class<?>... parameterTypes) throws Exception{
		this(Type.getInternalName(cls), cls.getDeclaredMethod(name, parameterTypes));
	}
	
	protected void visitMethodInsn(MethodVisitor visitor, int invokeType, boolean itf){
		visitor.visitMethodInsn(invokeType, path, name, des, itf);
	}
}

/**
 * 通过{@link String#valueOf(Object)}方法把其它类型的数据转成字段串
 * @author jackson
 *
 */
class StringDescriptor extends ASMMethod{
	
	protected StringDescriptor(Class<?> parameterType) throws Exception{
		super("java/lang/String", "valueOf");
		Method valueOf = null;
		try{
			valueOf = String.class.getDeclaredMethod(name, parameterType);
		}catch (Exception e) {
			//有两种基本类型是没有的、但可以直接使用int.class进行传值的；这两种类型是：short/byte
			valueOf = String.class.getDeclaredMethod(name, int.class);
		}
		des = Type.getMethodDescriptor(valueOf);
	}
	
	protected void visitMethodInsn(MethodVisitor visitor) {
		super.visitMethodInsn(visitor, Opcodes.INVOKESTATIC, false);
	}
}